﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace MyConsoleApp.Code
{
    /* 例子:
        A a1 = new B();
        a1.Name = "1a2b3c";

        B b1 = new B();
        ObjectConverter<A, B>.Express(a1, (o) => { b1 = o; });
      
        C c = new C();
        c.Name = "abc";
        A a2 = ObjectConverter<C, A>.Convert(c);
    */

    //利用表达式树转换T1至T2
    public class ObjectConverter<Tin, Tout>
    {
        static ObjectConverter()
        {
            //等号右边的参数或变量
            var valueExpr = Expression.Parameter(typeof(Tin), "value");

            //显示转换 相当于 (Tout)Tin
            var convertExpr = Expression.Convert(valueExpr, typeof(Tout));

            //生成Lambda
            var lambdaExpr1 = Expression.Lambda<Func<Tin, Tout>>(convertExpr, valueExpr);

            //编译成委托
            func1 = lambdaExpr1.Compile();

            //------------------------------------------------------------------------------
            //AS表达式,相当于 value as Tout
            var asExpr = Expression.TypeAs(valueExpr, typeof(Tout));

            //生成Lambda
            var lambdaExpr2 = Expression.Lambda<Func<Tin, Tout>>(asExpr, valueExpr);

            //编译成委托
            func2 = lambdaExpr2.Compile();
        }

        //缓存委托,多次调用时提高效率
        private static Func<Tin, Tout> func1;
        private static Func<Tin, Tout> func2;

        //转换方法1 直接转换(Tout)Tin
        public static Tout Convert(Tin value)
        {            
            return func1(value);
        }

        //转换方法2 Tin as Tout
        public static void Express(Tin t, Action<Tout> action)
        {
            //Lambda执行结果
            Tout result = func2.Invoke(t);

            if (result != null)
            {
                action(result);
            }
        }
    }

    //实体类DEMO
    public class A
    {
        public string Name { get; set; }
    }

    public class B : A
    {
        public string BName { get; set; }
    }

    public class C : A
    {
        public string Name { get; set; }
        public string CName { get; set; }
    }
}
